package com.songtech.base.universal.dao.util;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import javax.annotation.Resource;

import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import com.songtech.base.universal.dao.BaseDao;
import com.songtech.base.universal.dao.BaseDaoImpl;
import com.songtech.base.universal.service.BaseServiceImpl;

import javassist.CannotCompileException;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;

/**
 * 泛型DAO实例自动生成工具
 * <p>扫描所有Service中注入的DAO接口属性，spring默认不能直接注入， 需要为不同的泛型生成实例，此工具则用于自动化的解决此问题，减少手工编码
 * @author Alex.H
 */
@Component
public class DaoImplAutoCreator extends InstantiationAwareBeanPostProcessorAdapter implements BeanFactoryPostProcessor {
	protected DefaultListableBeanFactory beanFactory;
	
	protected ClassPool pool;
	
	// 实现类
	protected Class<?> cls;
	// 实现类
	protected String clsName;
	
	public DaoImplAutoCreator() {
//		this.cls = DaoImpl.class;
//		this.clsName = cls.getName();
		this.pool = ClassPool.getDefault();
		pool.insertClassPath(new ClassClassPath(DaoImplAutoCreator.class));
	}
	
	@Override
	public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
		Class<?> c = bean.getClass();
		
		// 通用的service只能在controller中使用
		Controller contro = c.getAnnotation(Controller.class);
		if (contro != null) {
			// 通用的service使用entity作为bean的名字，在方法中做特殊处理
			// 通用的service继承的基础dao，所以接口class使用Dao.class
			this.generatedClasses(c, BaseDao.class, BaseServiceImpl.class);
			return pvs;
		}
		
		// 只解析Service
		Service annotation = c.getAnnotation(Service.class);
		if (annotation == null) {
			return pvs;
		}
		
		this.generatedClasses(c, BaseDao.class, BaseDaoImpl.class);
		
		return pvs;
	}
	
	/**
	 * 动态生成类
	 * @param c
	 * @param interfaceCls
	 * @param implCls
	 */
	protected void generatedClasses(Class<?> c, Class<?> interfaceCls, Class<?> implCls){
		for (Field field : c.getDeclaredFields()) {
			// 要注入的bean的名字
			StringBuffer daoInjectionName = new StringBuffer();
			// dao的资源名称，如果是继承的基础dao，则获取注入的resource的name值
			StringBuffer resourceName = new StringBuffer();
			Class<?> modelClass = this.getFieldGenericType(interfaceCls, implCls, field, daoInjectionName, c, resourceName);
			
			// 验证泛型必须存在
			if (modelClass == null) {
				continue;
			}
			
			// 根据model类名生成dao类名
			String daoName;
			if(interfaceCls.getName().equals(BaseDao.class.getName())){
				daoName = "".equals(resourceName.toString()) ? this.createDaoImplName(modelClass) : resourceName.toString();
			}else{
				// 生成注入的service
				daoName = "".equals(resourceName.toString()) ? this.createServiceImplName(modelClass) : resourceName.toString();
			}
			
			// 如果bean已经存在，则无须再注册了
			if (this.checkBeanExists(daoInjectionName.toString())) {
				continue;
			}
			
			// 静态字节码方式生成dao实例类
			Class<?> daoImplClass = this.getDaoImplClass(daoName, modelClass);
			
			// 将新生成的dao类型注册为spring的bean
			this.registerBean(daoInjectionName.toString(), daoImplClass, modelClass);
		}
	}
	
	/**
	 * 获取属性的泛型类型
	 * @param interfaceCls
	 * @param implCls
	 * @param field
	 * @param daoInjectionName
	 * @param clazz
	 * @param resourceName
	 * @return
	 */
	protected Class<?> getFieldGenericType(Class<?> interfaceCls, Class<?> implCls, Field field, StringBuffer daoInjectionName, Class<?> clazz, StringBuffer resourceName) {
		Class<?> entityClass = null;
		if (field.getType().isAssignableFrom(interfaceCls)) {
			Type t = field.getGenericType();
			if (ParameterizedType.class.isAssignableFrom(t.getClass())) {
				entityClass = (Class<?>) ((ParameterizedType) t).getActualTypeArguments()[0];
				this.cls = implCls;
				this.clsName = cls.getName();
				daoInjectionName.append(entityClass.getName());// 注册的bin的名字
			}
		} else {
			// 如果实现的是接口
			Class<?>[] interfaces = field.getType().getInterfaces();
			for(Class<?> i : interfaces){
				if(i.isAssignableFrom(interfaceCls)){
					Type t = field.getGenericType();
					if (ParameterizedType.class.isAssignableFrom(t.getClass())) {
						try{
							entityClass = (Class<?>) ((ParameterizedType) t).getActualTypeArguments()[0];
						}catch (ClassCastException e) {
//							entityClass = this.getEntityClass(clazz);// 获取entity对象的class(未实现)
						}
						if(implCls.toString().equals(BaseServiceImpl.class.toString())){
							// 通用的service使用entity作为bean的名字，做特殊处理
							this.cls = implCls;
							this.clsName = cls.getName();
							daoInjectionName.append(entityClass.getName() + "_service");// 注册的bin的名字
						}else{
							this.cls = this.getInterfaceImpl(field, clazz, resourceName);
							
							if(cls != null){
								this.clsName = cls.getName();
								daoInjectionName.append(resourceName.toString());// 注册的bin的名字
							}
						}
					}
					break;
				}
			}
		}
		return entityClass;
	}
	
//	protected Class<?> getEntityClass(Class<?> clazz){
//		for (Field field : clazz.getDeclaredFields()) {
//			if(field.getType().toString().equals(Class.class.toString())){
//				beanFactory.getBean("com.songtech.universalservice.model.UniversalTest");
//				System.out.println();
////				return field.get(clazz)
//			}
//			System.out.println(field);
//		}
//		return null;
//	}
	
	/**
	 * 通过注解获取要注入的实现类
	 * @param field
	 * @param clazz
	 * @param resourceName
	 * @return
	 */
	protected Class<?> getInterfaceImpl(Field field, Class<?> clazz, StringBuffer resourceName) {
		Class<?> implClass = null;
		if(field.isAnnotationPresent(Resource.class)){
			// 属性的注解
			implClass = this.getImplClass(field, resourceName);
		}else{
			// set方法的注解
			try {
				// 通过属性获取set方法
				String methodName = field.getName();
				methodName = methodName.substring(0, 1).toUpperCase() + methodName.substring(1, methodName.length());
				// field.getType()：set方法中传入的参数
				Method m = clazz.getMethod("set" + methodName, field.getType());
				implClass = this.getImplClass(m, resourceName);
			} catch (NoSuchMethodException | SecurityException e) {
				e.printStackTrace();
			}
		}
		return implClass;
	}
	
	/**
	 * 获取要注入的实现类
	 * @param obj
	 * @param resourceName
	 */
	protected Class<?> getImplClass(Object obj, StringBuffer resourceName){
		Class<?> implClass = null;
		if(obj instanceof Field){
			// 获取属性的注入的名称
			if(((Field) obj).isAnnotationPresent(Resource.class)){
				resourceName.append(((Field) obj).getAnnotation(Resource.class).name());
			}
		}else if(obj instanceof Method){
			// 获取set方法的注入的名称
			if(((Method) obj).isAnnotationPresent(Resource.class)){
				resourceName.append(((Method) obj).getAnnotation(Resource.class).name());
			}
		}
		
		// 获取对应的要注入的实现类的class
		if(!"".equals(resourceName.toString())){
			try {
//				implClass = Class.forName(resourceName.substring(0, resourceName.length() - 2).toString());
				implClass = Class.forName(resourceName.toString());
			} catch (ClassNotFoundException e1) {
			}
		}
		return implClass;
	}
	
	/**
	 * 获取service实例的类名
	 * @param entityClass
	 * @return
	 */
	protected String createServiceImplName(Class<?> entityClass) {
		return entityClass.getName().replaceAll(".model.", ".service.") + "SERVICE";
	}
	
	/**
	 * 获取dao实例的类名
	 * @param entityClass
	 * @return
	 */
	protected String createDaoImplName(Class<?> entityClass) {
		return entityClass.getName().replaceAll(".model.", ".dao.") + "DAO";
	}
	
	/**
	 * 生成dao实例的class
	 * <p>这里使用javassist字节码生成工具生成class
	 * @param daoName
	 * @return
	 */
	protected Class<?> getDaoImplClass(String daoName, Class<?> entityClass) {
		Class<?> newDaoClass = null;
		
		try {
			newDaoClass = Class.forName(daoName);
		} catch (ClassNotFoundException e1) {
		}
		
		if (newDaoClass == null) {
			try {
				CtClass subClass = pool.makeClass(daoName);
				subClass.setSuperclass(pool.get(clsName));
				subClass.setGenericSignature("L" + clsName.replace('.', '/') + "<L" + entityClass.getName().replace('.', '/') + ";>;");
//				subClass = this.setBeans(subClass);// 添加注解
				subClass.writeFile();
				newDaoClass = subClass.toClass();

//				// 把生成的class文件写入文件
//				try {
//					byte[] byteArr = subClass.toBytecode();
//					java.io.FileOutputStream fos = new java.io.FileOutputStream(new java.io.File("E://" + subClass.getName() + "0.class"));
//					fos.write(byteArr);
//					fos.close();
//				} catch (IOException e) {
//					e.printStackTrace();
//				} catch (CannotCompileException e) {
//					e.printStackTrace();
//				}
			} catch (CannotCompileException | NotFoundException e) {
				e.printStackTrace();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
		
		return newDaoClass;
	}
	
	/**
	 * 给类添加注解
	 * @param subClass
	 * @return
	 */
	protected CtClass setBeans(CtClass subClass){
		ConstPool constPool = subClass.getClassFile().getConstPool();
        AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
        Annotation scopeAnnotation = new Annotation(Repository.class.getName(), constPool);
//        scopeAnnotation.addMemberValue("value", new StringMemberValue("BeanName", constPool));
        annotationsAttribute.addAnnotation(scopeAnnotation);
        subClass.getClassFile().addAttribute(annotationsAttribute);
        return subClass;
	}
	
	/**
	 * 检测bean是否存在
	 * @param daoName
	 * @return
	 */
	protected boolean checkBeanExists(String daoName) {
		return beanFactory.containsBeanDefinition(daoName);
	}
	
	/**
	 * 将dao实例class注册为spring的bean
	 * @param daoName 注册的bean名称
	 * @param daoClass dao的class类型
	 * @param entityClass dao映射的model类型
	 */
	protected void registerBean(String daoName, Class<?> daoClass, Class<?> entityClass) {
		BeanDefinitionBuilder bd = BeanDefinitionBuilder.genericBeanDefinition(daoClass);
		bd.addPropertyValue("entityClass", entityClass);
		bd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_NO);
		beanFactory.registerBeanDefinition(daoName, bd.getBeanDefinition());
	}
	
	/**
	 * 测试
	 */
	protected void test() {
		String[] names = beanFactory.getBeanNamesForType(BaseDao.class);
		System.out.println("====================================================");
		for (String name : names) {
			BeanDefinition definition = beanFactory.getBeanDefinition(name);
			System.out.println(definition.getBeanClassName());
		}
		System.out.println("====================================================");
	}
	
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		this.beanFactory = (DefaultListableBeanFactory) beanFactory;
	}
}
